﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Drawing.Imaging;

namespace GIMP
{
    class PI
    {
        /**
         * Método para converter uma imagem colorida em escala de cinza.
         * @param name="imagem" - imagem a ser convertida.
         * @param name="IncrementaBarraProgresso" - delegate para atualização da barra de progresso.
         * @returns nova imagem convertida em escala de cinza.
         */
        public static Bitmap PretoBranco(Bitmap imagem, DelegateProgressBar IncrementaBarraProgresso)
        {
            Bitmap nova_imagem = new Bitmap(imagem.Width, imagem.Height);
            for (int i = 0; i < imagem.Width; i++)
            {
                IncrementaBarraProgresso(1);
                for (int j = 0; j < imagem.Height; j++)
                {
                    Color cor_original = imagem.GetPixel(i, j);
                    byte escala_cinza = (byte)(
                        (cor_original.R * .299) +
                        (cor_original.G * .587) +
                        (cor_original.B * .114)
                    );
                    Color cor_nova = Color.FromArgb(escala_cinza, escala_cinza, escala_cinza);
                    nova_imagem.SetPixel(i, j, cor_nova);
                }
            }
            return nova_imagem;
        }

        /**
         * Método que ajusta uma imagem de acordo com alguma intensidade.
         * @param name="imagem" - imagem a ser ajustado o brilho.
         * @param name="intensidade" - intensidade do brilho a ser adicionado.
         * @param name="IncrementaBarraProgresso" - delegate para atualização da barra de progresso.
         * @returns nova imagem com o brilho ajustado.
         */
        public static Bitmap AjusteBrilho(Bitmap imagem, int intensidade, DelegateProgressBar IncrementaBarraProgresso)
        {
            Bitmap nova_imagem = new Bitmap(imagem.Width, imagem.Height);
            for (int i = 0; i < imagem.Width; i++)
            {
                IncrementaBarraProgresso(1);
                for (int j = 0; j < imagem.Height; j++)
                {
                    Color cor_original = imagem.GetPixel(i, j);
                    int novoR = cor_original.R + intensidade;
                    int novoG = cor_original.G + intensidade;
                    int novoB = cor_original.B + intensidade;
                    Color cor_nova = Color.FromArgb(
                        (novoR > 255) ? 255 : ((novoR < 0) ? 0 : novoR),
                        (novoG > 255) ? 255 : ((novoG < 0) ? 0 : novoG),
                        (novoB > 255) ? 255 : ((novoB < 0) ? 0 : novoB)
                    );
                    nova_imagem.SetPixel(i, j, cor_nova);
                }
            }
            return nova_imagem;
        }

        /**
         * Método que ajusta o contraste conforme o valor recebido por parâmetro.
         * @param name="imagem" - imagem a ser ajustado o contraste.
         * @param name="contraste" - contraste a ser adicionado na imagem.
         * @param name="IncrementaBarraProgresso" - delegate para atualização da barra de progresso.
         * @returns nova imagem com o contraste alterado.
         */
        public static Bitmap AjusteContraste(Bitmap imagem, int contraste, DelegateProgressBar IncrementaBarraProgresso)
        {
            Bitmap nova_imagem = new Bitmap(imagem.Width, imagem.Height);
            double fator = (100.0 + contraste) / 100.0;
            fator *= fator;
            for (int i = 0; i < imagem.Width; i++)
            {
                IncrementaBarraProgresso(1);
                for (int j = 0; j < imagem.Height; j++)
                {
                    Color cor_original = imagem.GetPixel(i, j);
                    // Para uma qualidade maior aplica-se os cálculos abaixo:
                    //double novoR = ((((cor_original.R / 255.0) - 0.5) * fator) + 0.5) * 255.0;
                    //double novoG = ((((cor_original.G / 255.0) - 0.5) * fator) + 0.5) * 255.0;
                    //double novoB = ((((cor_original.B / 255.0) - 0.5) * fator) + 0.5) * 255.0;
                    double novoR = cor_original.R * fator;
                    double novoG = cor_original.G * fator;
                    double novoB = cor_original.B * fator;
                    Color cor_nova = Color.FromArgb(
                        (novoR > 255) ? 255 : ((novoR < 0) ? 0 : (int)novoR),
                        (novoG > 255) ? 255 : ((novoG < 0) ? 0 : (int)novoG),
                        (novoB > 255) ? 255 : ((novoB < 0) ? 0 : (int)novoB)
                    );
                    nova_imagem.SetPixel(i, j, cor_nova);
                }
            }
            return nova_imagem;
        }

        /**
         * Método que gera o negativo de uma imagem.
         * @param name="imagem" imagem a ser alterada.
         * @param name="IncrementaBarraProgresso" - delegate para atualização da barra de progresso.
         * @returns nova imagem com o negativo gerado através da imagem original.
         */
        public static Bitmap Negativo(Bitmap imagem, DelegateProgressBar IncrementaBarraProgresso)
        {
            Bitmap nova_imagem = new Bitmap(imagem.Width, imagem.Height);
            for (int i = 0; i < imagem.Width; i++)
            {
                IncrementaBarraProgresso(1);
                for (int j = 0; j < imagem.Height; j++)
                {
                    Color cor_original = imagem.GetPixel(i, j);
                    Color cor_nova = Color.FromArgb(
                        (255 - cor_original.R),
                        (255 - cor_original.G),
                        (255 - cor_original.B)
                    );
                    nova_imagem.SetPixel(i, j, cor_nova);
                }
            }
            return nova_imagem;
        }

        /**
         * Método para obter o tom máximo e mínimo de acordo com o pixel de duas imagens.
         * Utilizado para o reescalonamento de valores.
         * param name="canal1" - um canal de um pixel de uma imagem.
         * param name="canal2" - um canal de um pixel de uma imagem.
         * returns dicionário contendo o tom máximo e o tom mínimo.
         */
        private static Dictionary<string, int> GetMaxAndMinRGB(Color pixel1, Color pixel2, char canal)
        {
            Dictionary<string, int> dict = new Dictionary<string, int>();
            dict.Add("tmin", 0);
            dict.Add("tmax", 255);
            canal = canal.ToString().ToUpper().ToCharArray()[0];

            if (canal.Equals("R"))
            {
                int r = pixel1.R;
                int r2 = pixel2.R;
                dict["tmin"] = (r2 < r) ? r2 : r;
                dict["tmax"] = (r2 > r) ? r2 : r;
                return dict;
            }
            if (canal.Equals("G"))
            {
                int g = pixel1.G;
                int g2 = pixel2.G;
                dict["tmin"] = (g2 < g) ? g2 : g;
                dict["tmax"] = (g2 > g) ? g2 : g;
                return dict;
            }
            if (canal.Equals("B"))
            {
                int b = pixel1.B;
                int b2 = pixel2.B;
                dict["tmin"] = (b2 < b) ? b2 : b;
                dict["tmax"] = (b2 > b) ? b2 : b;
                return dict;
            }
            return dict;
        }

        /**
         * Método que realiza a soma de duas imagens com reescalonamento de valores.
         * @param name="imagem" and name="imagem2" - imagens a ser somadas, podendo ser iguais ou não.
         * @param name="IncrementaBarraProgresso" - delegate para atualização da barra de progresso.
         * @returns nova imagem com o resultado da soma.
         */
        public static Bitmap Soma(Bitmap imagem, Bitmap imagem2, DelegateProgressBar IncrementaBarraProgresso)
        {
            int width = (imagem.Width < imagem2.Width) ? imagem.Width : imagem2.Width;
            int height = (imagem.Height < imagem2.Height) ? imagem.Height : imagem2.Height;
            Bitmap nova_imagem = new Bitmap(width, height);
            for (int i = 0; i < width; i++)
            {
                IncrementaBarraProgresso(1);
                for (int j = 0; j < height; j++)
                {
                    Color cor_original1 = imagem.GetPixel(i, j);
                    Color cor_original2 = imagem2.GetPixel(i, j);
                    // Obtem os valores máximo e mínimo de cada canal.
                    Dictionary<string, int> dictR = GetMaxAndMinRGB(cor_original1, cor_original2, 'R');
                    Dictionary<string, int> dictG = GetMaxAndMinRGB(cor_original1, cor_original2, 'G');
                    Dictionary<string, int> dictB = GetMaxAndMinRGB(cor_original1, cor_original2, 'B');
                    // Reescalonamento de valores.
                    double novoR = (255.0 / (dictR["tmax"] - dictR["tmin"]) * ((cor_original1.R + cor_original2.R) - dictR["tmin"]));
                    double novoG = (255.0 / (dictG["tmax"] - dictG["tmin"]) * ((cor_original1.G + cor_original2.G) - dictG["tmin"]));
                    double novoB = (255.0 / (dictB["tmax"] - dictB["tmin"]) * ((cor_original1.B + cor_original2.B) - dictB["tmin"]));
                    Color cor_nova = Color.FromArgb(
                        (novoR > 255) ? 255 : ((novoR < 0) ? 0 : (int)novoR),
                        (novoG > 255) ? 255 : ((novoG < 0) ? 0 : (int)novoG),
                        (novoB > 255) ? 255 : ((novoB < 0) ? 0 : (int)novoB)
                    );
                    nova_imagem.SetPixel(i, j, cor_nova);
                }
            }
            return nova_imagem;
        }

        /**
         * Método que através de uma imagem e de um canal calcula os valores para o histograma.
         * @param name="imagem" - imagem a ser analizada.
         * @param name="canal" - canal a ser analisado em cada pixel da imagem.
         * @param name="IncrementaBarraProgresso" - delegate para atualização da barra de progresso.
         * @returns array de inteiros com a contagem dos tons para montar o histograma.
         */
        public static int[] Histograma(Bitmap imagem, char canal)
        {
            int[] lista = new int[256];
            lista[0] = 0;
            for (int i = 0; i < imagem.Width; i++)
            {
                for (int j = 0; j < imagem.Height; j++)
                {
                    Color cor_original = imagem.GetPixel(i, j);
                    if (canal.Equals('R'))
                    {
                        lista[cor_original.R]++;
                    }
                    else
                    {
                        if (canal.Equals('G'))
                        {
                            lista[cor_original.G]++;
                        }
                        else
                        {
                            if (canal.Equals('B'))
                            {
                                lista[cor_original.B]++;
                            }
                            else
                            {
                                byte cor = (byte)(
                                    (cor_original.R * .299) +
                                    (cor_original.G * .587) +
                                    (cor_original.B * .114)
                                );
                                lista[cor]++;
                            }
                        }
                    }
                }
            }
            return lista;
        }

        /**
         * Método que realiza a equalização do histograma.
         * @param name="imagem" - imagem base para receber a equalização.
         * @param name="IncrementaBarraProgresso" - delegate para atualização da barra de progresso.
         * @returns nova imagem com a equalização.
         */
        public static Bitmap EqualizacaoHistograma(Bitmap imagem, DelegateProgressBar IncrementaBarraProgresso)
        {
            int[] histoR = Histograma(imagem, 'R');
            int[] histoG = Histograma(imagem, 'G');
            int[] histoB = Histograma(imagem, 'B');
            double[] hist_cumR = new double[256];
            double[] hist_cumG = new double[256];
            double[] hist_cumB = new double[256];
            double a = 255.0 / (imagem.Width * imagem.Height);
            hist_cumR[0] = (a * histoR[0]);
            hist_cumG[0] = (a * histoG[0]);
            hist_cumB[0] = (a * histoB[0]);

            for (int i = 1; i < 256; i++)
            {
                hist_cumR[i] = (hist_cumR[i - 1] + (a * histoR[i]));
                hist_cumG[i] = (hist_cumG[i - 1] + (a * histoG[i]));
                hist_cumB[i] = (hist_cumB[i - 1] + (a * histoB[i]));
            }

            Bitmap nova_imagem = new Bitmap(imagem.Width, imagem.Height);
            for (int i = 0; i < imagem.Width; i++)
            {
                IncrementaBarraProgresso(1);
                for (int j = 0; j < imagem.Height; j++)
                {
                    Color cor_original = imagem.GetPixel(i, j);
                    int novoR = (int)hist_cumR[cor_original.R];
                    int novoG = (int)hist_cumG[cor_original.G];
                    int novoB = (int)hist_cumB[cor_original.B];
                    Color cor_nova = Color.FromArgb(
                        (novoR > 255) ? 255 : ((novoR < 0) ? 0 : novoR),
                        (novoG > 255) ? 255 : ((novoG < 0) ? 0 : novoG),
                        (novoB > 255) ? 255 : ((novoB < 0) ? 0 : novoB)
                    );
                    nova_imagem.SetPixel(i, j, cor_nova);
                }
            }
            return nova_imagem;
        }

        /**
         * Método que realiza a média entre duas imagens.
         * @param name="imagem" and name="imagem2" - imagens a ser somadas
         * @param name="IncrementaBarraProgresso" - delegate para atualização da barra de progresso.
         * @returns nova imagem com o resultado da média.
         */
        public static Bitmap Media(Bitmap imagem, Bitmap imagem2, DelegateProgressBar IncrementaBarraProgresso)
        {
            int width = (imagem.Width < imagem2.Width) ? imagem.Width : imagem2.Width;
            int height = (imagem.Height < imagem2.Height) ? imagem.Height : imagem2.Height;
            Bitmap nova_imagem = new Bitmap(width, height);
            for (int i = 0; i < width; i++)
            {
                IncrementaBarraProgresso(1);
                for (int j = 0; j < height; j++)
                {
                    Color cor_original1 = imagem.GetPixel(i, j);
                    Color cor_original2 = imagem2.GetPixel(i, j);
                    double novoR = (cor_original1.R + cor_original2.R) / 2.0;
                    double novoG = (cor_original1.G + cor_original2.G) / 2.0;
                    double novoB = (cor_original1.B + cor_original2.B) / 2.0;
                    Color cor_nova = Color.FromArgb(
                        (novoR > 255) ? 255 : ((novoR < 0) ? 0 : (int)novoR),
                        (novoG > 255) ? 255 : ((novoG < 0) ? 0 : (int)novoG),
                        (novoB > 255) ? 255 : ((novoB < 0) ? 0 : (int)novoB)
                    );
                    nova_imagem.SetPixel(i, j, cor_nova);
                }
            }
            return nova_imagem;
        }

        /**
         * Método que realiza a subtração entre duas imagens.
         * @param name="imagem" and name="iamgem2" - imagens a serem subtraídas.
         * @param name="IncrementaBarraProgresso" - delegate para atualização da barra de progresso.
         * @returns nova imagem com o resultado da subtração.
         */
        public static Bitmap Subtracao(Bitmap imagem, Bitmap imagem2, DelegateProgressBar IncrementaBarraProgresso)
        {
            int width = (imagem.Width < imagem2.Width) ? imagem.Width : imagem2.Width;
            int height = (imagem.Height < imagem2.Height) ? imagem.Height : imagem2.Height;
            Bitmap nova_imagem = new Bitmap(width, height);
            for (int i = 0; i < width; i++)
            {
                IncrementaBarraProgresso(1);
                for (int j = 0; j < height; j++)
                {
                    Color cor_original1 = imagem.GetPixel(i, j);
                    Color cor_original2 = imagem2.GetPixel(i, j);
                    double novoR = (cor_original1.R - cor_original2.R);
                    double novoG = (cor_original1.G - cor_original2.G);
                    double novoB = (cor_original1.B - cor_original2.B);
                    Color cor_nova = Color.FromArgb(
                        (novoR > 255) ? 255 : ((novoR < 0) ? 0 : (int)novoR),
                        (novoG > 255) ? 255 : ((novoG < 0) ? 0 : (int)novoG),
                        (novoB > 255) ? 255 : ((novoB < 0) ? 0 : (int)novoB)
                    );
                    nova_imagem.SetPixel(i, j, cor_nova);
                }
            }
            return nova_imagem;
        }

        /**
         * Método que realiza a operação lógica "OR" entre duas imagens
         * @param name="imagem1" - imagem para operação lógica.
         * @param name="imagem2" - imagem para operação lógica.
         * @param name="IncrementaBarraProgresso" - delegate para atualização da barra de progresso.
         * @returns nova imagem com o resultado da operação.
         */
        public static Bitmap Or(Bitmap imagem1, Bitmap imagem2, DelegateProgressBar IncrementaBarraProgresso)
        {
            int width = (imagem1.Width < imagem2.Width) ? imagem1.Width : imagem2.Width;
            int height = (imagem1.Height < imagem2.Height) ? imagem1.Height : imagem2.Height;
            Bitmap nova_imagem = new Bitmap(width, height);
            for (int i = 0; i < width; i++)
            {
                IncrementaBarraProgresso(1);
                for (int j = 0; j < height; j++)
                {
                    int cor = imagem1.GetPixel(i, j).ToArgb() | imagem2.GetPixel(i, j).ToArgb();
                    nova_imagem.SetPixel(i, j, Color.FromArgb(cor));
                }
            }
            return nova_imagem;
        }

        /**
         * Método que realiza a operação lógica "AND" entre duas imagens
         * @param name="imagem1" - imagem para operação lógica.
         * @param name="imagem2" - imagem para operação lógica.
         * @param name="IncrementaBarraProgresso" - delegate para atualização da barra de progresso.
         * @returns nova imagem com o resultado da operação.
         */
        public static Bitmap And(Bitmap imagem1, Bitmap imagem2, DelegateProgressBar IncrementaBarraProgresso)
        {
            int width = (imagem1.Width < imagem2.Width) ? imagem1.Width : imagem2.Width;
            int height = (imagem1.Height < imagem2.Height) ? imagem1.Height : imagem2.Height;
            Bitmap nova_imagem = new Bitmap(width, height);
            for (int i = 0; i < width; i++)
            {
                IncrementaBarraProgresso(1);
                for (int j = 0; j < height; j++)
                {
                    int cor = imagem1.GetPixel(i, j).ToArgb() & imagem2.GetPixel(i, j).ToArgb();
                    nova_imagem.SetPixel(i, j, Color.FromArgb(cor));
                }
            }
            return nova_imagem;
        }
        /*
        * Método que realiza a Limiarização entre as imagens
        * @param imagem - Imagem que será limiarizada
        * @param t - inteiro que dará o limite para a limiar
        * @param IncrementaBarraProgresso - Delegate para a barra de status
        * @result Bitmap - Imagem resultado
        */
        public static Bitmap Limiarizacao(Bitmap imagem, int t, DelegateProgressBar IncrementaBarraProgresso)
        {
            Bitmap nova_imagem = new Bitmap(imagem.Width, imagem.Height);
            for (int i = 0; i < imagem.Width; i++)
            {
                IncrementaBarraProgresso(1);
                for (int j = 0; j < imagem.Height; j++)
                {
                    Color cor_original = imagem.GetPixel(i, j);
                    int novoR = (cor_original.R > t) ? 255 : 0;
                    int novoG = (cor_original.G > t) ? 255 : 0;
                    int novoB = (cor_original.B > t) ? 255 : 0;
                    Color cor_nova = Color.FromArgb(novoR, novoG, novoB);
                    nova_imagem.SetPixel(i, j, cor_nova);
                }
            }
            return nova_imagem;
        }

        /*
         * Método que realiza a Limiarização entre diversas variáveis entre as imagens
         * @param imagem - Imagem que será limiarizada
         * @param  x - posição x na imagem para determinar o pixel que será analisado
         * @param y - posição y na imagem para determinar o pixel que será analisado
         * @param t - inteiro que dará o limite para a limiar
         * @param IncrementaBarraProgresso - Delegate para a barra de status
         * @returns Bitmap - Imagem resultado
         */
        public static Bitmap LimiarizacaoDV(Bitmap imagem, Color pixel_clique, int t, DelegateProgressBar IncrementaBarraProgresso)
        {
            Bitmap nova_imagem = new Bitmap(imagem.Width, imagem.Height);
            for (int i = 0; i < imagem.Width; i++)
            {
                IncrementaBarraProgresso(1);
                for (int j = 0; j < imagem.Height; j++)
                {
                    Color cor_original = imagem.GetPixel(i, j);
                    Color cor_nova = new Color();
                    if (DistanciaPontos(pixel_clique, cor_original) <= t)
                    {
                        cor_nova = pixel_clique;
                    }
                    else
                    {
                        cor_nova = Color.White;
                    }
                    nova_imagem.SetPixel(i, j, cor_nova);    
                }
            }
            return nova_imagem;
        }

        /*
         * Método que calcula a distância entre cada canal de cada cor, para obter-se a distancia entre as duas cores.
         * 
         * @param cor - Primeira cor a ser comparada
         * @param cor2 - Segunda cor a ser comparada
         * @returns d - distância entre as cores
         */
        private static int DistanciaPontos(Color cor, Color cor2)
        {
            return (int)Math.Sqrt(Math.Pow(cor.R - cor2.R, 2) + Math.Pow(cor.G - cor2.G, 2) + Math.Pow(cor.B - cor2.B, 2));
        }

        /*
         * Método que realiza o crescimento de regiões dado o local de clique na imagem.
         * @param clique - local de clique na imagem
         * @param IncrementaBarraProgresso - Delegate para a barra de status
         * @returns Bitmap - Imagem resultado
         */
        public static Bitmap CrescimentoRegioes(Bitmap imagem, Point clique, int fator, DelegateProgressBar IncrementaBarraProgresso)
        {
            Color pixel_clique = imagem.GetPixel(clique.X, clique.Y);
            Bitmap nova_imagem = new Bitmap(imagem.Width, imagem.Height);
            for (int i = 1; i < imagem.Width; i++)
            {
                IncrementaBarraProgresso(1);
                for (int j = 1; j < imagem.Height; j++)
                {
                    Color cor_nova = new Color();
                    if (DistanciaPontos(imagem.GetPixel(i, j), pixel_clique) <= fator)
                    {
                        cor_nova = Color.Black;
                    }
                    else
                    {
                        cor_nova = imagem.GetPixel(i, j);
                    }
                    nova_imagem.SetPixel(i, j, cor_nova);
                }
            }
            return nova_imagem;
        }

        /**
         * Método que realiza o filtro de média na imagem utilizando de uma máscara de tamanho dinâmico.
         * 
         * @param name="imagem" - imagem para as operações.
         * @param name="larguraM" - largura da matriz de máscara.
         * @param name="alturaM" - altura da matriz de máscara.
         * @param name="IncrementaBarraProgresso" - incremento a barra de progresso na tela.
         * @returns nova imagem com o filtro aplicado.
         */
        public static Bitmap FiltroMedia(Bitmap imagem, int larguraM, int alturaM, DelegateProgressBar IncrementaBarraProgresso)
        {
            Bitmap nova_imagem = new Bitmap(imagem.Width, imagem.Height);
            int max_i = (larguraM >> 1);
            int max_j = (alturaM >> 1);
            for (int i = max_i; i < (imagem.Width - max_i); i++)
            {
                IncrementaBarraProgresso(1);
                for (int j = max_j; j < (imagem.Height - max_j); j++)
                {
                    int init_i = (i <= larguraM) ? i : (i - larguraM);
                    int init_j = (j <= alturaM) ? j : (j - alturaM);

                    int somaR = 0;
                    int somaG = 0;
                    int somaB = 0;
                    // iterações para os vizinhos.
                    for (int vi = i - max_i; vi < i + max_i + 1; vi++)
                    {
                        for (int vj = j - max_j; vj < j + max_j + 1; vj++)
                        {
                            somaR += imagem.GetPixel(vi, vj).R;
                            somaG += imagem.GetPixel(vi, vj).G;
                            somaB += imagem.GetPixel(vi, vj).B;
                        }
                    }
                    int areaM = larguraM * alturaM;
                    int mediaR = somaR / areaM;
                    int mediaG = somaG / areaM;
                    int mediaB = somaB / areaM;
                    Color cor_nova = Color.FromArgb(mediaR, mediaG, mediaB);
                    nova_imagem.SetPixel(i, j, cor_nova);
                }
            }
            return nova_imagem;
        }

        /**
         * Método que realiza o filtro de mediana na imagem utilizando de uma máscara de tamanho dinâmico.
         * 
         * @param name="imagem" - imagem para as operações.
         * @param name="larguraM" - largura da matriz de máscara.
         * @param name="alturaM" - altura da matriz de máscara.
         * @param name="IncrementaBarraProgresso" - incremento a barra de progresso na tela.
         * @returns nova imagem com o filtro aplicado.
         */
        public static Bitmap FiltroMediana(Bitmap imagem, int larguraM, int alturaM, DelegateProgressBar IncrementaBarraProgresso)
        {
            Bitmap nova_imagem = new Bitmap(imagem.Width, imagem.Height);
            int max_i = (larguraM >> 1);
            int max_j = (alturaM >> 1);
            for (int i = max_i; i < (imagem.Width - max_i); i++)
            {
                IncrementaBarraProgresso(1);
                for (int j = max_j; j < (imagem.Height - max_j); j++)
                {
                    List<int> coresR = new List<int>();
                    List<int> coresG = new List<int>();
                    List<int> coresB = new List<int>();

                    for (int vi = i - max_i; vi < i + max_i + 1; vi++)
                    {
                        for (int vj = j - max_j; vj < j + max_j + 1; vj++)
                        {
                            coresR.Add(imagem.GetPixel(vi, vj).R);
                            coresG.Add(imagem.GetPixel(vi, vj).G);
                            coresB.Add(imagem.GetPixel(vi, vj).B);
                        }
                    }
                    coresR.Sort();
                    coresG.Sort();
                    coresB.Sort();
                    int medio = coresB.Count >> 1;
                    int novoR = coresR.ElementAt(medio);
                    int novoG = coresG.ElementAt(medio);
                    int novoB = coresB.ElementAt(medio);
                    Color cor_nova = Color.FromArgb(novoR, novoG, novoB);
                    nova_imagem.SetPixel(i, j, cor_nova);
                }
            }
            return nova_imagem;
        }

        /**
         * Método que aplica o filtro de passa alta básico em uma imagem.
         * 
         * @param name="imagem" - imagem para as operações.
         * @param name="IncrementaBarraProgresso" - incremento a barra de progresso na tela.
         * @returns nova imagem com o filtro aplicado.
         */
        public static Bitmap FiltroPassaAltaBasico(Bitmap imagem, DelegateProgressBar IncrementaBarraProgresso)
        {
            int[,] mascara = {{-1, -1, -1},
                              {-1,  8, -1},
                              {-1, -1, -1}
                             };
            Bitmap nova_imagem = new Bitmap(imagem.Width, imagem.Height);
            for (int i = 1; i < imagem.Width - 2; i++)
            {
                IncrementaBarraProgresso(1);
                for (int j = 1; j < imagem.Height - 2; j++)
                {
                    int somaR = 0;
                    int somaG = 0;
                    int somaB = 0;
                    // iterações para os vizinhos.
                    for (int vi = i - 1; vi < i + 2; vi++)
                    {
                        for (int vj = j - 1; vj < j + 2; vj++)
                        {
                            int elemMascara = mascara[vi - i + 1, vj - j + 1];
                            somaR += imagem.GetPixel(vi, vj).R * elemMascara;
                            somaG += imagem.GetPixel(vi, vj).G * elemMascara;
                            somaB += imagem.GetPixel(vi, vj).B * elemMascara;
                        }
                    }
                    somaR = (somaR < 0) ? 0 : (somaR > 255) ? 255 : somaR;
                    somaG = (somaG < 0) ? 0 : (somaG > 255) ? 255 : somaG;
                    somaB = (somaB < 0) ? 0 : (somaB > 255) ? 255 : somaB;
                    Color cor_nova = Color.FromArgb(somaR, somaG, somaB);
                    nova_imagem.SetPixel(i, j, cor_nova);
                }
            }
            return nova_imagem;
        }

        /**
         * Método que aplica o filtro de alto reforço em uma imagem.
         * 
         * @param name="imagem" - imagem para as operações.
         * @param name="IncrementaBarraProgresso" - incremento a barra de progresso na tela.
         * @returns nova imagem com o filtro aplicado.
         */
        public static Bitmap FiltroAltoReforco(Bitmap imagem, double fator, DelegateProgressBar IncrementaBarraProgresso)
        {
            fator = (100.0 + fator) / 100.0;
            double cons = -(1 / 9);
            // Máscata para o alto reforço.
            double[,] mascara = {{cons,                cons, cons},
                                 {cons, (9 * fator - 1) / 9, cons},
                                 {cons,                cons, cons}
                                };
            Bitmap nova_imagem = new Bitmap(imagem.Width, imagem.Height);
            for (int i = 1; i < imagem.Width - 2; i++)
            {
                IncrementaBarraProgresso(1);
                for (int j = 1; j < imagem.Height - 2; j++)
                {
                    double somaR = 0;
                    double somaG = 0;
                    double somaB = 0;
                    // iterações para os vizinhos.
                    for (int vi = i - 1; vi < i + 2; vi++)
                    {
                        for (int vj = j - 1; vj < j + 2; vj++)
                        {
                            double elemMascara = mascara[vi - i + 1, vj - j + 1];
                            somaR += imagem.GetPixel(vi, vj).R * elemMascara;
                            somaG += imagem.GetPixel(vi, vj).G * elemMascara;
                            somaB += imagem.GetPixel(vi, vj).B * elemMascara;
                        }
                    }
                    somaR = (somaR < 0) ? 0 : (somaR > 255) ? 255 : somaR;
                    somaG = (somaG < 0) ? 0 : (somaG > 255) ? 255 : somaG;
                    somaB = (somaB < 0) ? 0 : (somaB > 255) ? 255 : somaB;
                    Color cor_nova = Color.FromArgb((int)somaR, (int)somaG, (int)somaB);
                    nova_imagem.SetPixel(i, j, cor_nova);
                }
            }
            return nova_imagem;
        }

        /**
         * Método que aplica o filtro de Sobel em uma imagem.
         * 
         * @param name="imagem" - imagem para as operações.
         * @param name="IncrementaBarraProgresso" - incremento a barra de progresso na tela.
         * @returns nova imagem com o filtro aplicado.
         */
        public static Bitmap FiltroSobel(Bitmap imagem, DelegateProgressBar IncrementaBarraProgresso)
        {
            Bitmap nova_imagem = new Bitmap(imagem.Width, imagem.Height);
            // Máscara horizontal.
            int[,] mascara = {{-1, -2, -1},
                              {0,   0,  0},
                              {1,   2,  1}
                             };
            // Máscara vertical.
            int[,] mascarav = {{-1, 0, 1},
                               {-2, 0, 2},
                               {-1, 0, 1}
                              };

            for (int i = 1; i < imagem.Width - 2; i++)
            {
                IncrementaBarraProgresso(1);
                for (int j = 1; j < imagem.Height - 2; j++)
                {
                    int somaR = 0;
                    int somaG = 0;
                    int somaB = 0;
                    // iterações para os vizinhos.
                    for (int vi = i - 1; vi < i + 2; vi++)
                    {
                        for (int vj = j - 1; vj < j + 2; vj++)
                        {
                            int elemMascara = mascara[vi - i + 1, vj - j + 1];
                            int elemMascarav = mascarav[vi - i + 1, vj - j + 1];
                            somaR += imagem.GetPixel(vi, vj).R * elemMascara + imagem.GetPixel(vi, vj).R * elemMascarav;
                            somaG += imagem.GetPixel(vi, vj).G * elemMascara + imagem.GetPixel(vi, vj).R * elemMascarav;
                            somaB += imagem.GetPixel(vi, vj).B * elemMascara + imagem.GetPixel(vi, vj).R * elemMascarav;
                        }
                    }
                    somaR = (somaR < 0) ? 0 : (somaR > 255) ? 255 : somaR;
                    somaG = (somaG < 0) ? 0 : (somaG > 255) ? 255 : somaG;
                    somaB = (somaB < 0) ? 0 : (somaB > 255) ? 255 : somaB;
                    Color cor_nova = Color.FromArgb(somaR, somaG, somaB);
                    nova_imagem.SetPixel(i, j, cor_nova);
                }
            }
            return nova_imagem;
        }

        /**
         * Método que aplica o filtro de Prewitt em uma imagem.
         * 
         * @param name="imagem" - imagem para as operações.
         * @param name="IncrementaBarraProgresso" - incremento a barra de progresso na tela.
         * @returns nova imagem com o filtro aplicado.
         */
        public static Bitmap FiltroPrewitt(Bitmap imagem, DelegateProgressBar IncrementaBarraProgresso)
        {
            Bitmap nova_imagem = new Bitmap(imagem.Width, imagem.Height);
            // Máscara horizontal.
            int[,] mascara1 = {{-1, -1, -1},
                              {0,   0,  0},
                              {1,   1,  1}
                             };
            // Máscara vertical.
            int[,] mascara2 = {{-1, 0, 1},
                               {-1, 0, 1},
                               {-1, 0, 1}
                              };

            for (int i = 1; i < imagem.Width - 2; i++)
            {
                IncrementaBarraProgresso(1);
                for (int j = 1; j < imagem.Height - 2; j++)
                {
                    int somaR = 0;
                    int somaG = 0;
                    int somaB = 0;
                    // iterações para os vizinhos.
                    for (int vi = i - 1; vi < i + 2; vi++)
                    {
                        for (int vj = j - 1; vj < j + 2; vj++)
                        {
                            int elemMascara1 = mascara1[vi - i + 1, vj - j + 1];
                            int elemMascara2 = mascara2[vi - i + 1, vj - j + 1];
                            somaR += imagem.GetPixel(vi, vj).R * elemMascara1 + imagem.GetPixel(vi, vj).R * elemMascara1;
                            somaG += imagem.GetPixel(vi, vj).G * elemMascara1 + imagem.GetPixel(vi, vj).R * elemMascara1;
                            somaB += imagem.GetPixel(vi, vj).B * elemMascara1 + imagem.GetPixel(vi, vj).R * elemMascara1;
                        }
                    }
                    somaR = (somaR < 0) ? 0 : (somaR > 255) ? 255 : somaR;
                    somaG = (somaG < 0) ? 0 : (somaG > 255) ? 255 : somaG;
                    somaB = (somaB < 0) ? 0 : (somaB > 255) ? 255 : somaB;
                    Color cor_nova = Color.FromArgb(somaR, somaG, somaB);
                    nova_imagem.SetPixel(i, j, cor_nova);
                }
            }
            return nova_imagem;
        }

        /**
         * Método que aplica o filtro de Roberts em uma imagem.
         * 
         * @param name="imagem" - imagem para as operações.
         * @param name="IncrementaBarraProgresso" - incremento a barra de progresso na tela.
         * @returns nova imagem com o filtro aplicado.
         */
        public static Bitmap FiltroRoberts(Bitmap imagem, DelegateProgressBar IncrementaBarraProgresso)
        {
            Bitmap nova_imagem = new Bitmap(imagem.Width, imagem.Height);
            // Máscara horizontal.
            int[,] mascara1 = {{1,  0},
                               {0, -1}
                              };
            // Máscara Vertical.
            int[,] mascara2 = {{ 0, 1},
                               {-1, 0}
                              };

            for (int i = 1; i < imagem.Width - 2; i++)
            {
                IncrementaBarraProgresso(1);
                for (int j = 1; j < imagem.Height - 2; j++)
                {
                    int somaR = 0;
                    int somaG = 0;
                    int somaB = 0;
                    // iterações para os vizinhos.
                    for (int vi = i - 1; vi < i + 2; vi++)
                    {
                        for (int vj = j - 1; vj < j + 2; vj++)
                        {
                            somaR += imagem.GetPixel(vi, vj).R * mascara1[0, 0] + imagem.GetPixel(vi, vj + 1).R * mascara1[0, 1] + imagem.GetPixel(vi + 1, vj).R * mascara1[1, 0] + imagem.GetPixel(vi + 1, vj + 1).R * mascara1[1, 1];
                            somaR += imagem.GetPixel(vi, vj).R * mascara2[0, 0] + imagem.GetPixel(vi, vj + 1).R * mascara2[0, 1] + imagem.GetPixel(vi + 1, vj).R * mascara2[1, 0] + imagem.GetPixel(vi + 1, vj + 1).R * mascara2[1, 1];
                            somaG += imagem.GetPixel(vi, vj).G * mascara1[0, 0] + imagem.GetPixel(vi, vj + 1).G * mascara1[0, 1] + imagem.GetPixel(vi + 1, vj).G * mascara1[1, 0] + imagem.GetPixel(vi + 1, vj + 1).G * mascara1[1, 1];
                            somaG += imagem.GetPixel(vi, vj).G * mascara2[0, 0] + imagem.GetPixel(vi, vj + 1).G * mascara2[0, 1] + imagem.GetPixel(vi + 1, vj).G * mascara2[1, 0] + imagem.GetPixel(vi + 1, vj + 1).G * mascara2[1, 1];
                            somaB += imagem.GetPixel(vi, vj).B * mascara1[0, 0] + imagem.GetPixel(vi, vj + 1).B * mascara1[0, 1] + imagem.GetPixel(vi + 1, vj).B * mascara1[1, 0] + imagem.GetPixel(vi + 1, vj + 1).B * mascara1[1, 1];
                            somaB += imagem.GetPixel(vi, vj).B * mascara2[0, 0] + imagem.GetPixel(vi, vj + 1).B * mascara2[0, 1] + imagem.GetPixel(vi + 1, vj).B * mascara2[1, 0] + imagem.GetPixel(vi + 1, vj + 1).B * mascara2[1, 1];

                        }
                    }
                    somaR = (somaR < 0) ? 0 : (somaR > 255) ? 255 : somaR;
                    somaG = (somaG < 0) ? 0 : (somaG > 255) ? 255 : somaG;
                    somaB = (somaB < 0) ? 0 : (somaB > 255) ? 255 : somaB;
                    Color cor_nova = Color.FromArgb(somaR, somaG, somaB);
                    nova_imagem.SetPixel(i, j, cor_nova);
                }
            }
            return nova_imagem;
        }
    }
}
